﻿using System;
// 默认情况下，类型参数可以被任何类型替换。
// 在类型参数上应用约束，可以定义类型参数为指定类型。
// 下面是可用的约束：
/*
 where T : base-class // 基类约束
 where T : interface // 接口约束
 where T : class // 引用类型约束
 where T : struct // 数值类型约束（排除可空类型）
 where T : new() // 无参数构造方法约束
 where U : T // 裸类型约束

 */
namespace _070_泛化的约束
{
    class SomeClass { }
    interface Interface1 { }
    //下面的示例GenericClass<T,U>，要求T派生自SomeClass且实现接口Interface1，要求U提供无参数构造方法：
    class GenericClass<T, U> where T : SomeClass, Interface1
                             where U : new()
    { }
    class Program
    {
        // 约束可以应用在方法和类的任何类型参数的定义中。
        // 基类约束或接口约束规定类型参数必须是某个类的子类或实现特定类或接口。这允许参数类可以被隐式转换成特定类或接口。
        //例如，假定写一个泛化方法Max，返回两个值中的较大者。我们可以利用框架中定义的泛化接口IComparable<T>:
        /*
         public interface IComparable<T> //接口的简化写法
         {
            int CompareTo(T other);
          }
         */
        //如果other比this大，CompareTo，CompareTo方法返回正值。用此接口作为约束，我们可以写如下的Max方法（为了避免分散注意力，省略了null值检查）：
        static T Max<T>(T a, T b) where T : IComparable<T>
        {
            return a.CompareTo(b) > 0 ? a : b;
        }

        static void Main(string[] args)
        {
            int z = Max(5, 10); // 10
            string last = Max("ant", "zoo"); // zoo
            Console.WriteLine(last);
            // 类约束和结构体约束规定T必须是引用类型或数值类型（不能为空）。
            // 结构体约束的一个很好的示例是System.Nullable<T>结构体：
            //struct Nullable<T> where T : struct {...}

            Console.ReadKey();
        }

        // 无参数构造方法约束要求T有一个公有的无参数构造方法。
        // 如果定义了这个约束，就可以在T中调用new()：
        static void Initialize<T>(T[] array) where T : new()
        {
            for (int i = 0; i < array.Length; i++)
            {
                array[i] = new T();
            }
        }

        // 裸类型约束要求一个类型参数从另一个类型参数派生。
        //本例中，方法FilteredStack返回另一个Stack，返回Stack只包含原类中满足U条件的部分元素、
        class Stack<T>
        {
            Stack<U> FilterStack<U>() where U : T { return default(Stack<U>); }
        }
    }
}